package cn.stylefeng.guns.modular.gridsystem.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.guns.core.exception.ServiceException;
import cn.stylefeng.guns.core.factory.PageFactory;
import cn.stylefeng.guns.core.pojo.page.PageResult;
import cn.stylefeng.guns.modular.gridsystem.constants.GatewayTaskStatusConstant;
import cn.stylefeng.guns.modular.gridsystem.constants.GatewayTaskTypeConstant;
import cn.stylefeng.guns.modular.gridsystem.constants.SystemConstant;
import cn.stylefeng.guns.modular.gridsystem.entity.BizGateway;
import cn.stylefeng.guns.modular.gridsystem.entity.GatewayTask;
import cn.stylefeng.guns.modular.gridsystem.entity.GatewayTaskRecord;
import cn.stylefeng.guns.modular.gridsystem.enums.GatewayExceptionEnum;
import cn.stylefeng.guns.modular.gridsystem.enums.GatewayTaskExceptionEnum;
import cn.stylefeng.guns.modular.gridsystem.enums.SystemParamExceptionEnum;
import cn.stylefeng.guns.modular.gridsystem.mapper.BizGatewayMapper;
import cn.stylefeng.guns.modular.gridsystem.mapper.GatewayTaskMapper;
import cn.stylefeng.guns.modular.gridsystem.mapper.GatewayTaskRecordMapper;
import cn.stylefeng.guns.modular.gridsystem.model.param.GatewayTaskParam;
import cn.stylefeng.guns.modular.gridsystem.model.result.GatewayTaskResult;
import cn.stylefeng.guns.modular.gridsystem.service.GatewayTaskService;
import cn.stylefeng.guns.modular.gridsystem.utils.OptionalBean;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * 网关任务下发表 服务实现类
 *
 * @author zxw
 * @date 2022/10/31 14:34
 */
@Service
@Slf4j
public class GatewayTaskServiceImpl extends ServiceImpl<GatewayTaskMapper, GatewayTask> implements GatewayTaskService {

    @Resource
    private GatewayTaskRecordMapper recordMapper;
    @Resource
    private BizGatewayMapper gatewayMapper;

    /**
     * 分页查询网关任务下发表
     *
     * @param param
     * @return
     */
    @Override
    public PageResult<GatewayTaskResult> page(GatewayTaskParam param) {
        return new PageResult<>(this.baseMapper.pagelist(PageFactory.defaultPage(), param));
    }

    /**
     * 分页查询网关任务下发表
     *
     * @param param
     * @return
     */
    @Override
    public PageResult<GatewayTaskResult> pageTaskList(GatewayTaskParam param) {
        return new PageResult<>(this.baseMapper.pageTaskList(PageFactory.defaultPage(), param));
    }

    /**
     * 添加网关任务下发表
     *
     * @param param
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(GatewayTaskParam param) {
        GatewayTask task = BeanUtil.toBean(param, GatewayTask.class);
        task.setTaskStatus(GatewayTaskStatusConstant.WAIT_ISSUED);
        // 保存任务信息
        this.save(task);
        // 保存任务对应的网关信息
        for (Integer gatewayId : param.getGatewayIds()) {
            GatewayTaskRecord record = GatewayTaskRecord.builder().taskId(task.getId()).gatewayId(gatewayId).taskStatus(GatewayTaskStatusConstant.WAIT_ISSUED).build();
            recordMapper.insert(record);
        }
    }

    /**
     * 添加任务
     *
     * @param taskType  任务类型
     * @param gatewayId 网关集合
     * @param taskParam 任务参数
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long add(String taskType, Integer gatewayId, String taskParam) {
        GatewayTask task = GatewayTask.builder().taskType(taskType).taskStatus(GatewayTaskStatusConstant.ISSUED).issuedTime(DateTime.now()).build();
        if (ObjectUtil.isNotEmpty(taskParam)) {
            task.setTaskParams(taskParam);
        }
        this.save(task);
        if (ObjectUtil.isEmpty(gatewayId)) {
            log.warn("关联网关不能为空");
            throw new ServiceException(GatewayTaskExceptionEnum.GATEWAY_IDS_NOT_ALLOWED_EMPTY);
        }
        // 保存任务对应的网关信息
        GatewayTaskRecord record = GatewayTaskRecord.builder().taskId(task.getId()).gatewayId(gatewayId).taskStatus(GatewayTaskStatusConstant.ISSUED).build();
        recordMapper.insert(record);
        return ObjectUtil.isEmpty(task.getId()) ? 0 : task.getId();
    }

    /**
     * 添加任务
     *
     * @param taskType 任务类型
     * @param gatewayIds 网关id
     * @param taskParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long add(String taskType, List<Integer> gatewayIds, String taskParam) {
        GatewayTask task = GatewayTask.builder()
                .taskType(taskType)
                .taskStatus(GatewayTaskStatusConstant.ISSUED)
                .issuedTime(DateTime.now()).build();
        if (ObjectUtil.isNotEmpty(taskParam)) {
            task.setTaskParams(taskParam);
        }
        save(task);
        if (ObjectUtil.isEmpty(gatewayIds) || gatewayIds.isEmpty()) {
            throw new ServiceException(GatewayTaskExceptionEnum.GATEWAY_IDS_NOT_ALLOWED_EMPTY);
        }
        // 保存任务对应的网关信息
        for (Integer gatewayId : gatewayIds) {
            GatewayTaskRecord record = GatewayTaskRecord.builder()
                    .taskId(task.getId())
                    .gatewayId(gatewayId)
                    .taskStatus(GatewayTaskStatusConstant.ISSUED).build();
            recordMapper.insert(record);
        }
        return ObjectUtil.isEmpty(task.getId()) ? 0 : task.getId();
    }


    /**
     * 删除网关任务下发表
     *
     * @param gatewayTaskParam id 网关任务id
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(GatewayTaskParam gatewayTaskParam) {
        // 删除子表信息
        recordMapper.delete(new LambdaUpdateWrapper<GatewayTaskRecord>().eq(GatewayTaskRecord::getTaskId, gatewayTaskParam.getId()));
        // 删除网关任务表
        removeById(gatewayTaskParam.getId());
    }

    /**
     * 编辑网关任务下发表
     *
     * @param param id 主键
     *              taskType 任务类型
     *              taskStatus 待下发状态
     *              taskParams 任务附加参数
     *              stationId 站点ID
     *              gatewayId 网关ID
     *              gatewayCode 上报网关的编号
     *              startTime 开始时间
     *              endTime 结束时间
     *              responseResult 任务响应结果
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(GatewayTaskParam param) {
        // 根据id查询实体
        GatewayTask gatewayTask = queryGatewayTask(param);
        // 删除子表信息 只能删除待下发的
        recordMapper.delete(new LambdaUpdateWrapper<GatewayTaskRecord>().eq(GatewayTaskRecord::getTaskId, gatewayTask.getId()));
        if (!GatewayTaskStatusConstant.WAIT_ISSUED.equals(gatewayTask.getTaskStatus())) {
            throw new ServiceException(GatewayTaskExceptionEnum.WAIT_ISSUED_CAN_EDIT);
        }
        // 请求参数转化为实体
        BeanUtil.copyProperties(param, gatewayTask);
        updateById(gatewayTask);
        // 保存子表信息
        for (Integer gatewayId : param.getGatewayIds()) {
            GatewayTaskRecord record = GatewayTaskRecord.builder().taskId(gatewayTask.getId()).taskStatus(GatewayTaskStatusConstant.WAIT_ISSUED).gatewayId(gatewayId).build();
            recordMapper.insert(record);
        }

    }

    /**
     * 下发任务
     *
     * @param param 查询应撤回的任务
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void issued(GatewayTaskParam param) {
        // 根据id查询实体
        GatewayTask gatewayTask = queryGatewayTask(param);
        //查询任务状态
        if (!GatewayTaskStatusConstant.WAIT_ISSUED.equals(gatewayTask.getTaskStatus())) {
            throw new ServiceException(GatewayTaskExceptionEnum.TASK_STATUS_NOT_WAIT_ISSUED);
        }
        gatewayTask.setTaskStatus(GatewayTaskStatusConstant.ISSUED);
        gatewayTask.setIssuedTime(new Date());
        updateById(gatewayTask);
        // 查询子表数据
        List<GatewayTaskRecord> records = recordMapper.selectList(new LambdaQueryWrapper<GatewayTaskRecord>().eq(GatewayTaskRecord::getTaskId, gatewayTask.getId()));
        if (ObjectUtil.isNotEmpty(records)) {
            for (GatewayTaskRecord record : records) {
                //设置要下发的任务
                record.setTaskStatus(GatewayTaskStatusConstant.ISSUED);
                recordMapper.updateById(record);
            }
        }
    }

    /**
     * 撤回任务
     *
     * @param param 查询应撤回的任务
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollback(GatewayTaskParam param) {
        OptionalBean.ofNullable(param).orElseThrow(() -> new ServiceException(SystemParamExceptionEnum.PARAM_NULL_ERROR));
        // 根据id查询应该撤回的任务
        GatewayTask gatewayTask = queryGatewayTask(param);
        //撤回已下发的任务
        if (!GatewayTaskStatusConstant.ISSUED.equals(gatewayTask.getTaskStatus())) {
            throw new ServiceException(GatewayTaskExceptionEnum.ISSUED_TASK_CAN_ROLLBACK);
        }
        // 不能撤回自动下发的任务
        if (GatewayTaskTypeConstant.UPDATE_FIRMWARE.equals(gatewayTask.getTaskType()) || GatewayTaskTypeConstant.UPLOAD_NEW_FILE.equals(gatewayTask.getTaskType()) || GatewayTaskTypeConstant.REGISTER_MODEL.equals(gatewayTask.getTaskType()) || GatewayTaskTypeConstant.SYNC_MODEL.equals(gatewayTask.getTaskType())) {
            throw new ServiceException(GatewayTaskExceptionEnum.CANNOT_ROLLBACK_AUTO_TASK);
        }
        // 不能撤回已有子表设备响应的任务
        if (recordMapper.selectCount(new LambdaQueryWrapper<GatewayTaskRecord>().eq(GatewayTaskRecord::getTaskId, gatewayTask.getId()).eq(GatewayTaskRecord::getTaskStatus, GatewayTaskStatusConstant.RESPONSE)) > 0) {
            throw new ServiceException(GatewayTaskExceptionEnum.CANNOT_ROLLBACK_RESPONSE_TASK);
        }
        gatewayTask.setTaskStatus(GatewayTaskStatusConstant.WAIT_ISSUED);
        gatewayTask.setIssuedTime(DateTime.now());
        updateById(gatewayTask);
        // 查询子表数据
        List<GatewayTaskRecord> records = recordMapper.selectList(new LambdaQueryWrapper<GatewayTaskRecord>().eq(GatewayTaskRecord::getTaskId, gatewayTask.getId()));
        if (ObjectUtil.isNotEmpty(records)) {
            for (GatewayTaskRecord record : records) {
                record.setTaskStatus(GatewayTaskStatusConstant.WAIT_ISSUED);
                recordMapper.updateById(record);
            }
        }
    }

    /**
     * 查看详情网关任务下发表
     *
     * @param gatewayTaskParam id 主键
     * @return GatewayTask 网关任务下发表
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    public GatewayTask detail(GatewayTaskParam gatewayTaskParam) {
        return queryGatewayTask(gatewayTaskParam);
    }

    /**
     * 根据id获取网关任务下发表
     *
     * @param gatewayTaskParam id 主键
     * @return GatewayTask 网关任务下发表
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    private GatewayTask queryGatewayTask(GatewayTaskParam gatewayTaskParam) {
        OptionalBean.ofNullable(gatewayTaskParam).orElseThrow(() -> new ServiceException(SystemParamExceptionEnum.PARAM_NULL_ERROR));
        GatewayTask gatewayTask = getById(gatewayTaskParam.getId());
        OptionalBean.ofNullable(gatewayTask).orElseThrow(() -> new ServiceException(GatewayTaskExceptionEnum.NOT_EXISTED));
        return gatewayTask;
    }

    /**
     * 查询当天已下发状态的任务
     *
     * @param gatewayTaskParam gatewayCode 上报网关的编号
     *                         taskStatus 任务状态
     * @return List<GatewayTask> 下发的任务
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    public List<GatewayTask> getTodayGatewayTask(GatewayTaskParam gatewayTaskParam) {
        OptionalBean.ofNullable(gatewayTaskParam).orElseThrow(() -> new ServiceException(SystemParamExceptionEnum.PARAM_NULL_ERROR));
        gatewayTaskParam.setTaskStatus(GatewayTaskStatusConstant.ISSUED);
        return baseMapper.getTodayGatewayTask(gatewayTaskParam);
    }

    /**
     * 上报任务执行的回执
     *
     * @param param id  主键id
     *              gatewayCode 上报网关的编号
     *              responseResult  任务响应结果
     * @author 乔李莱莱
     * @since 2023/10/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callbackGatewayTask(GatewayTaskParam param) {
        //判断入参为空
        OptionalBean.ofNullable(param).orElseThrow(() -> new ServiceException(SystemParamExceptionEnum.PARAM_NULL_ERROR));
        // 根据id查询实体
        GatewayTask gatewayTask = getById(param.getId());
        //判断下发任务是否存在
        OptionalBean.ofNullable(gatewayTask).orElseThrow(() -> new ServiceException(GatewayTaskExceptionEnum.NOT_EXISTED));
        //判断下发任务状态是不是已下发
        if (!ObjectUtil.equals(GatewayTaskStatusConstant.ISSUED, gatewayTask.getTaskStatus())) {
            throw new ServiceException(GatewayTaskExceptionEnum.TASK_STATUS_NOT_ALREADY_ISSUED);
        }
        //设备是否存在
        BizGateway gateway = gatewayMapper.selectOne(new LambdaQueryWrapper<BizGateway>().eq(BizGateway::getGatewayCode, param.getGatewayCode()).last(SystemConstant.SQL_LIMIT));
        OptionalBean.ofNullable(gateway).orElseThrow(() -> new ServiceException(GatewayExceptionEnum.CODE_ERROR));
        // 修改任务状态
        GatewayTaskRecord record = recordMapper.selectOne(new LambdaQueryWrapper<GatewayTaskRecord>().eq(GatewayTaskRecord::getTaskId, gatewayTask.getId()).eq(GatewayTaskRecord::getGatewayId, gateway.getId()).last(SystemConstant.SQL_LIMIT));
        record.setTaskStatus(GatewayTaskStatusConstant.RESPONSE);
        record.setResponseTime(DateTime.now());
        record.setResponseResult(param.getResponseResult());
        recordMapper.updateById(record);
        // 查询所有的关联网关，如果所有关联网关都已响应，则将任务状态修改为已响应
        LambdaQueryWrapper<GatewayTaskRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(GatewayTaskRecord::getTaskId, gatewayTask.getId());
        lambdaQueryWrapper.ne(GatewayTaskRecord::getTaskStatus, GatewayTaskStatusConstant.RESPONSE);
        int selectedCount = recordMapper.selectCount(lambdaQueryWrapper);
        if (SystemConstant.NUM_0.equals(selectedCount)) {
            // 修改状态
            gatewayTask.setTaskStatus(GatewayTaskStatusConstant.RESPONSE);
            updateById(gatewayTask);
        }
    }
}
